using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using MyClever.Lib;
using System.Diagnostics;


namespace MyCleverMissionEditor
{
    public partial class PackageManager : Form
    {
        private struct PackageInfo
        {
            public int pluginProxyID;
            public string packageName;
        }
        
        private Mission mission;
        private MissionPluginProxy pluginToEdit;
        private int packageIndex;

        private Form1 missionEditorForm = null;
        public Form1 MissionEditorForm
        {
            get { return this.missionEditorForm; }
            set { this.missionEditorForm = value; }
        }

        private MissionPluginPackage CurrentPackage
        {
            get { return this.pluginToEdit.GetPackages(Direction.Input)[this.packageIndex]; }
        }
        

        /// <summary>
        /// Initializes a new instance of the <see cref="PackageManager"/> class.
        /// </summary>
        /// <param name="mission">The current mission we work on.</param>
        /// <param name="pluginToEdit">The plugin we like to add to the mission.</param>
        public PackageManager(ref Mission mission, int pluginToEdit)
        {
            InitializeComponent();

            this.mission = mission;
            this.pluginToEdit = this.mission.GetPluginByID(pluginToEdit);
            this.packageIndex = 0;
        }

        /// <summary>
        /// Handles the Load event of the PackageManager control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void PackageManager_Load(object sender, EventArgs e)
        {
            this.UpdateForNextStep();
        }

        /// <summary>
        /// Returns all packages from this mission, which are compatible with the given <see cref="originPackage"/>.
        /// </summary>
        /// <param name="originPackage">The package, you search compatible packages for.</param>
        /// <returns></returns>
        private Dictionary<string, PackageInfo> GetCompatiblePackages(MissionPluginPackage originPackage)
        {
            Dictionary<string, PackageInfo> packages = new Dictionary<string, PackageInfo>();

            // get all plugins in the current mission
            List<MissionPluginProxy> plugins = new List<MissionPluginProxy>();
            this.mission.PluginCollection.GetPlugins(ref plugins);

            foreach (MissionPluginProxy pluginOn in plugins)
            {
                // ignore the plugin itself, we search compatible packages for
                if (pluginOn.ID != this.pluginToEdit.ID)
                {
                    // run through every package to look of the current plugin
                    foreach (MissionPluginPackage packageOn in pluginOn.GetPackages(Direction.All))
                    {
                        if (true == originPackage.IsCompatible(packageOn))
                        {
                            PackageInfo compatiblePackage;
                            compatiblePackage.pluginProxyID = pluginOn.ID;
                            compatiblePackage.packageName = packageOn.TableName;
                            packages.Add(String.Format("{0}->{1}.{2}", pluginOn.ID, pluginOn.ClassName, packageOn.TableName), compatiblePackage);
                        }
                    }
                }
            }
            return packages;
        }

        private void UpdateForNextStep()
        {
            this.lblPackageName.Text = this.CurrentPackage.TableName;
            this.Text = String.Format("On which data should the package '{0}' depend?", this.lblPackageName.Text);

            this.cbBxCompatiblePackages.Items.Add("manually");
            this.cbBxCompatiblePackages.SelectedIndex = 0;

            // fill the comboBox with all possible package-streams
            // while the comboBox response a string as item, we make a workaround and save much more content in the background
            foreach (KeyValuePair<string, PackageInfo> packageInfoOn in this.GetCompatiblePackages(this.CurrentPackage))
            {
                this.cbBxCompatiblePackages.Items.Add(packageInfoOn.Key);
            }
          
            // show the current package in the dataGridView1
            this.dataGridView1.DataSource = this.CurrentPackage;
            this.MarkReferencedColumns();    
            this.dataGridView1.ReadOnly = false;

            // enable/disable next and back btns
            this.btnNextPackage.Enabled = (this.packageIndex < this.pluginToEdit.GetPackages(Direction.Input).Count - 1);
            this.btnLastPackage.Enabled = (this.packageIndex > 0);
        }

        private void btnNextPackage_Click(object sender, EventArgs e)
        {
            this.packageIndex++;
            this.UpdateForNextStep();
        }


        private void btnLastPackage_Click(object sender, EventArgs e)
        {
            this.packageIndex--;
            this.UpdateForNextStep();
        }

        private void btnAbort_Click(object sender, EventArgs e)
        {

        }

        private void btnSave_Click(object sender, EventArgs e)
        {
            this.MissionEditorForm.UpdateForm();
            this.Close();
        }

        private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
        {
            // Don't try to validate the 'new row' until finished editing since there
            if (true == this.dataGridView1.Rows[e.RowIndex].IsNewRow)
            {
                return;
            }
            
            MissionPluginPackage packageOn = (MissionPluginPackage)dataGridView1.DataSource;
            // attention: we get a valid packageOn, when the user edit the packages itself only
            if (null != packageOn && true == packageOn.Columns[e.ColumnIndex].ExtendedProperties.ContainsKey(MissionPluginPackageColumn.ExtendedPropertiesKey.Validation) && null != packageOn.Columns[e.ColumnIndex].ExtendedProperties[MissionPluginPackageColumn.ExtendedPropertiesKey.Validation])
            {
                if (false == ValidationService.Instance.IsValid(packageOn.Columns[e.ColumnIndex].ExtendedProperties[MissionPluginPackageColumn.ExtendedPropertiesKey.Validation].ToString(), e.FormattedValue.ToString()))
                {
                    this.dataGridView1.Rows[e.RowIndex].ErrorText = ValidationService.Instance.ErrorMessages;
                    e.Cancel = true;
                }
            }   
        }

        private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            // Clear the row error in case the user presses ESC.   
            this.dataGridView1.Rows[e.RowIndex].ErrorText = String.Empty;
        }

        private void dataGridView1_DragDrop(object sender, DragEventArgs e)
        {
            throw new AccessViolationException();
            
            //Dictionary<string, object> droppedData = (Dictionary<string, object>)e.Data.GetData(typeof(Dictionary<string, object>));

            //MissionPluginPackage linkedPackage;
            //MissionPluginPackageColumn linkedColumn;
            //this.mission.PluginCollection.GetReferenceInstance(droppedData, out linkedPackage, out linkedColumn);

            //// is the current package a soft linked package?
            //if (true == this.CurrentPackage.IsSoftPackage)
            //{
            //    // is the dragged object an package link?
            //    if (false == droppedData.ContainsKey("FromColumn"))
            //    {
            //        this.CurrentPackage.Clear();
            //        this.CurrentPackage.Columns.Clear();
            //        this.CurrentPackage.Reference = droppedData;
            //        this.CurrentPackage.CopyDataFromPackage(linkedPackage);
            //    }
            //    // or is this an package column link?
            //    else
            //    {
            //        MissionPluginPackageColumn clone = linkedColumn.Clone();
            //        clone.ColumnReference = droppedData;
            //        this.CurrentPackage.Columns.Add(clone);
            //    }
            //}
            //// ist the current package a hard linked package?
            //else
            //{
            //    // the dragged object must be an package column link!!
            //    Debug.Assert(true == droppedData.ContainsKey("FromColumn"));

            //    Point p = this.dataGridView1.PointToClient(new Point(e.X, e.Y));
            //    DataGridView.HitTestInfo hit = this.dataGridView1.HitTest(p.X, p.Y);
            //    MissionPluginPackageColumn hitColumn = this.CurrentPackage.GetColumnByName(this.dataGridView1.Columns[hit.ColumnIndex].Name);
            //    hitColumn.ColumnReference = droppedData;
            //}

            //this.dataGridView1.DataSource = this.CurrentPackage;
            //this.MarkReferencedColumns();
            //this.dataGridView1.Refresh();
            //this.dataGridView1.ReadOnly = true;
        }

        private void dataGridView1_DragEnter(object sender, DragEventArgs e)
        {
            if (true == e.Data.GetDataPresent(typeof(Dictionary<string, object>)))
            {
                e.Effect = DragDropEffects.Move;

                // is the dragged object an package link, than you you can only drop this link, if the current package is NOT a hard linked package
                Dictionary<string, object> droppedData = (Dictionary<string, object>)e.Data.GetData(typeof(Dictionary<string, object>));
                if (false == this.CurrentPackage.IsSoftPackage && false == droppedData.ContainsKey("FromColumn"))
                {
                    e.Effect = DragDropEffects.None;
                }
            }
        }

        private void MarkReferencedColumns()
        {
            DataGridViewCellStyle cellStyle = new DataGridViewCellStyle();
            cellStyle.BackColor = Color.OrangeRed;
            cellStyle.ForeColor = Color.Snow;

            foreach (MissionPluginPackageColumn columnOn in this.CurrentPackage.Columns)
            {
                // mark all linked datastream-columns with a special color
                if (true == columnOn.HasColumnReference || true == this.CurrentPackage.HasReference)
                {
                    this.dataGridView1.Columns[columnOn.ColumnName].HeaderCell.Style = cellStyle;
                }
                this.dataGridView1.Columns[columnOn.ColumnName].ToolTipText = columnOn.Description;
            }
            this.dataGridView1.EnableHeadersVisualStyles = false;
        }
    }
}